#include "preferencesmodel.h"
#include "utilities/qutils.h"
#include "utilities/settings.h"
#include "utilities/messageforwarder.h"
#include "jasptheme.h"
#include "utilities/languagemodel.h"
#include <QFontDatabase>
#include "modules/ribbonmodel.h"
#include "emptyvalues.h"
#include "preferencesmodelbase.h"
#include <QQuickWindow>
#include "githubpat.h"
#include "gui/jaspConfiguration/jaspconfiguration.h"

using namespace std;


PreferencesModel::PreferencesModel(QObject *parent) :
	PreferencesModelBase(parent)
{	
	connect(this,					&PreferencesModel::useDefaultPPIChanged,		this, &PreferencesModel::onUseDefaultPPIChanged			);
	connect(this,					&PreferencesModel::defaultPPIChanged,			this, &PreferencesModel::onDefaultPPIChanged			);
	connect(this,					&PreferencesModel::customPPIChanged,			this, &PreferencesModel::onCustomPPIChanged				);
	connect(this,					&PreferencesModel::useDefaultPPIChanged,		this, &PreferencesModel::plotPPIPropChanged				);
	connect(this,					&PreferencesModel::defaultPPIChanged,			this, &PreferencesModel::plotPPIPropChanged				);
	connect(this,					&PreferencesModel::customPPIChanged,			this, &PreferencesModel::plotPPIPropChanged				);
	connect(this,					&PreferencesModel::plotBackgroundChanged,		this, &PreferencesModel::whiteBackgroundChanged			);
	connect(this,					&PreferencesModel::modulesRememberChanged,		this, &PreferencesModel::resetRememberedModules			);
	connect(this,					&PreferencesModel::currentThemeReady,			this, &PreferencesModel::currentJaspThemeChanged,		Qt::QueuedConnection);
	connect(this,					&PreferencesModel::safeGraphicsChanged,			this, &PreferencesModel::animationsOnChanged			); // So animationsOn *might* not be changed, but it  doesnt matter
	connect(this,					&PreferencesModel::disableAnimationsChanged,	this, &PreferencesModel::animationsOnChanged			);
	connect(this,					&PreferencesModel::dataLabelNAChanged,			this, &PreferencesModel::dataLabelNAChangedSlot			);
	connect(this,					&PreferencesModel::guiQtTextRenderChanged,		this, &PreferencesModel::onGuiQtTextRenderChanged,		Qt::QueuedConnection);
	connect(this,					&PreferencesModel::developerModeChanged,		this, [&](){ this->setEngineSandbox(!this->developerMode()); } );
	
	connect(LanguageModel::lang(),	&LanguageModel::currentLanguageChanged,			this, &PreferencesModel::languageCodeChanged			);

	_loadDatabaseFont();

	for(auto pdfPageSizeElt : pdfPageSizeToVector())
	{
		QMap<QString, QVariant> map =
		{
			std::make_pair("value", int(pdfPageSizeElt)),
			std::make_pair("label", pdfPageSizeToQString(pdfPageSizeElt)),
		};

		_pdfPageSizeModel.append(map);
	}

	dataLabelNAChangedSlot(dataLabelNA());
}

void PreferencesModel::browseSpreadsheetEditor()
{
	
	QString filter = "File Description (*.*)";
	QString applicationfolder;

#ifdef _WIN32
	applicationfolder = "c:\\Program Files";
#elif __APPLE__
	applicationfolder = "/Applications";
#else
	applicationfolder = "/usr/bin";
#endif

	QString filename = MessageForwarder::browseOpenFile(tr("Select a file..."), applicationfolder, filter);

	if (filename != "")
		setCustomEditor(filename);
	
}

void PreferencesModel::browseDeveloperFolder()
{
	QString defaultfolder = developerFolder();
	if (defaultfolder.isEmpty())
	{
#ifdef _WIN32
		defaultfolder = "c:\\";
#else
		defaultfolder = "~";
#endif
	}

	QString folder = MessageForwarder::browseOpenFolder(tr("Select a folder..."), defaultfolder);

	if (!folder.isEmpty())
		setDeveloperFolder(folder);	
}


void PreferencesModel::browseDeveloperLibPathFolder()
{
	QString defaultfolder = directLibpathFolder();
	if (defaultfolder.isEmpty())
	{
#ifdef _WIN32
		defaultfolder = "c:\\";
#else
		defaultfolder = "~";
#endif
	}

	QString folder = MessageForwarder::browseOpenFolder(tr("Select a R-library..."), defaultfolder);

	if (!folder.isEmpty())
		setDirectLibpathFolder(folder);
}

void PreferencesModel::browseConfigurationFile()
{
	QString defaultfolder = JASPConfiguration::getInstance()->getDefaultConfigurationPath();
	if (defaultfolder.isEmpty())
	{
#ifdef _WIN32
		defaultfolder = "c:\\";
#else
		defaultfolder = "~";
#endif
	}

	QString folder = MessageForwarder::browseOpenFile(tr("Select a file..."), defaultfolder, "");

	if (!folder.isEmpty())
	{
		setLocalConfigurationPATH(folder);
		JASPConfiguration::getInstance()->processConfiguration();
	}

}


#define GET_PREF_FUNC(TYPE, NAME, SETTING, TO_FUNC)	TYPE PreferencesModel::NAME() const { return Settings::value(SETTING).TO_FUNC; }
#define GET_PREF_FUNC_BOOL(NAME, SETTING)					GET_PREF_FUNC(bool,		NAME, SETTING, toBool())
#define GET_PREF_FUNC_INT(NAME, SETTING)					GET_PREF_FUNC(int,		NAME, SETTING, toInt())
#define GET_PREF_FUNC_STR(NAME, SETTING)					GET_PREF_FUNC(QString,	NAME, SETTING, toString())
#define GET_PREF_FUNC_DBL(NAME, SETTING)					GET_PREF_FUNC(double,	NAME, SETTING, toDouble())
#define GET_PREF_FUNC_FLT(NAME, SETTING)					GET_PREF_FUNC(float,	NAME, SETTING, toFloat())
#define GET_PREF_FUNC_WHT(NAME, SETTING)					GET_PREF_FUNC(bool,		NAME, SETTING, toString() == "white")


GET_PREF_FUNC_BOOL(	fixedDecimals,				Settings::FIXED_DECIMALS							)
GET_PREF_FUNC_INT(	numDecimals,				Settings::NUM_DECIMALS								)
GET_PREF_FUNC_BOOL(	exactPValues,				Settings::EXACT_PVALUES								)
GET_PREF_FUNC_BOOL(	normalizedNotation,			Settings::NORMALIZED_NOTATION						)
GET_PREF_FUNC_FLT(	ribbonBarHeightScale,		Settings::RIBBON_BAR_HEIGHT_SCALE					)
GET_PREF_FUNC_BOOL(	useDefaultEditor,			Settings::USE_DEFAULT_SPREADSHEET_EDITOR			)
GET_PREF_FUNC_STR(	customEditor,				Settings::SPREADSHEET_EDITOR_NAME					)
GET_PREF_FUNC_STR(	developerFolder,			Settings::DEVELOPER_FOLDER							)
GET_PREF_FUNC_BOOL(	useDefaultPPI,				Settings::PPI_USE_DEFAULT							)
GET_PREF_FUNC_INT(	customPPI,					Settings::PPI_CUSTOM_VALUE							)
GET_PREF_FUNC_WHT(	whiteBackground,			Settings::IMAGE_BACKGROUND							)
GET_PREF_FUNC_STR(	plotBackground,				Settings::IMAGE_BACKGROUND							)
GET_PREF_FUNC_BOOL(	developerMode,				Settings::DEVELOPER_MODE							)
GET_PREF_FUNC_INT(	thresholdScale,				Settings::THRESHOLD_SCALE							)
GET_PREF_FUNC_BOOL(	logToFile,					Settings::LOG_TO_FILE								)
GET_PREF_FUNC_INT(	logFilesMax,				Settings::LOG_FILES_MAX								)
GET_PREF_FUNC_INT(	maxFlickVelocity,			Settings::QML_MAX_FLICK_VELOCITY					)
GET_PREF_FUNC_BOOL(	modulesRemember,			Settings::MODULES_REMEMBER							)
GET_PREF_FUNC_BOOL(	safeGraphics,				Settings::SAFE_GRAPHICS_MODE						)
GET_PREF_FUNC_STR(	cranRepoURL,				Settings::CRAN_REPO_URL								)
GET_PREF_FUNC_BOOL(	githubPatUseDefault,		Settings::GITHUB_PAT_USE_DEFAULT					)
GET_PREF_FUNC_STR(	currentThemeName,			Settings::THEME_NAME								)
GET_PREF_FUNC_BOOL(	useNativeFileDialog,		Settings::USE_NATIVE_FILE_DIALOG					)
GET_PREF_FUNC_BOOL(	disableAnimations,			Settings::DISABLE_ANIMATIONS						)
GET_PREF_FUNC_BOOL(	generateMarkdown,			Settings::GENERATE_MARKDOWN_HELP					)
GET_PREF_FUNC_INT(	maxEnginesAdmin,            Settings::MAX_ENGINE_COUNT_ADMIN                    )
GET_PREF_FUNC_BOOL( windowsNoBomNative,			Settings::WINDOWS_NO_BOM_NATIVE						)
GET_PREF_FUNC_INT(	windowsChosenCodePage,      Settings::WINDOWS_CHOSEN_CODEPAGE                   )
GET_PREF_FUNC_BOOL( dbShowWarning,				Settings::DB_SHOW_WARNING							)
GET_PREF_FUNC_STR(  dataLabelNA,				Settings::DATA_LABEL_NA								)
GET_PREF_FUNC_BOOL( guiQtTextRender,			Settings::GUI_USE_QT_TEXTRENDER						)
GET_PREF_FUNC_BOOL( reportingMode,				Settings::REPORT_SHOW								)
GET_PREF_FUNC_BOOL( showRSyntax,				Settings::SHOW_RSYNTAX								)
GET_PREF_FUNC_BOOL( showAllROptions,			Settings::SHOW_ALL_R_OPTIONS						)
GET_PREF_FUNC_BOOL( showRSyntaxInResults,		Settings::SHOW_RSYNTAX_IN_RESULTS					)
GET_PREF_FUNC_BOOL( ALTNavModeActive,			Settings::ALTNAVMODE_ACTIVE							)
GET_PREF_FUNC_BOOL( orderByValueByDefault,		Settings::ORDER_BY_VALUE_BY_DEFAULT					)
GET_PREF_FUNC_BOOL( checkUpdatesAskUser,		Settings::CHECK_UPDATES_ASK_USER					)
GET_PREF_FUNC_BOOL( checkUpdates,				Settings::CHECK_UPDATES								)
GET_PREF_FUNC_INT(	maxScaleLevels,				Settings::MAX_SCALE_LEVELS							)
GET_PREF_FUNC_BOOL(	pdfLandscape,				Settings::PDF_LANDSCAPE								)
GET_PREF_FUNC_INT(	pdfPageSize,				Settings::PDF_PAGESIZE								)
GET_PREF_FUNC_BOOL( directLibpathEnabled,		Settings::DIRECT_LIBPATH_ENABLED					)
GET_PREF_FUNC_STR(	directLibpathFolder,		Settings::DIRECT_LIBPATH_FOLDER						)
GET_PREF_FUNC_STR(	directDevModName,			Settings::DIRECT_DEVMOD_NAME						)
GET_PREF_FUNC_STR(	localConfigurationPATH,		Settings::LOCAL_CONFIGURATION_PATH              	)
GET_PREF_FUNC_BOOL(	remoteConfiguration,		Settings::REMOTE_CONFIGURATION                     	)
GET_PREF_FUNC_STR(	remoteConfigurationURL,		Settings::REMOTE_CONFIGURATION_URL					)
GET_PREF_FUNC_BOOL(	useConfigurationFile,		Settings::USE_CONFIGURATION_FILE					)
GET_PREF_FUNC_BOOL(	startMaximized,				Settings::START_MAXIMIZED							)
GET_PREF_FUNC_BOOL(	storeStateEtc,				Settings::STORE_STATE_ETC							)

GET_PREF_FUNC_BOOL(	autoSaveAtAll,				Settings::AUTOSAVE_ON								)
GET_PREF_FUNC_INT(	autoSaveIntervalSec,		Settings::AUTOSAVE_INTERVAL_SEC						)

bool PreferencesModel::engineSandbox() const
{
#ifdef _WIN32
	return Settings::value(Settings::ENGINE_SANDBOX).toBool();
#else
	return false;
#endif
}



int PreferencesModel::maxEngines() const
{
	int maxEngines = Settings::value(Settings::MAX_ENGINE_COUNT).toInt();

	if(maxEnginesAdmin() > 0)	return std::min(maxEngines, maxEnginesAdmin());
	else						return maxEngines;

}

QString PreferencesModel::githubPatCustom() const
{
	return 	decrypt(Settings::value(Settings::GITHUB_PAT_CUSTOM).toString());
}


double PreferencesModel::uiScale()
{
	if (_uiScale < 0)
		_uiScale = Settings::value(Settings::UI_SCALE).toDouble();
	return _uiScale;
}

QStringList PreferencesModel::emptyValues()		const
{
	return _splitValues(Settings::value(Settings::EMPTY_VALUES_LIST).toString());
}

QStringList PreferencesModel::modulesRemembered()	const
{
	QStringList items = Settings::value(Settings::MODULES_REMEMBERED).toString().split("|");

	return items;
}

void PreferencesModel::moduleEnabledChanged(QString moduleName, bool enabled)
{
	QStringList list = modulesRemembered();

	if(list.contains(moduleName) != enabled)
	{
		if(enabled)	list.append(moduleName);
		else		list.removeAll(moduleName);
	}

	setModulesRemembered(list);
}

QString PreferencesModel::languageCode() const
{
	return LanguageModel::lang()->currentLanguageCode();
}

bool PreferencesModel::useThousandSeparators() const
{
	return LanguageModel::lang()->useThousandSeps();
}


const QLocale & PreferencesModel::localeQt() const
{
	return LanguageModel::lang()->currentLocale();
}

QString PreferencesModel::githubPatResolved() const
{
	if(githubPatUseDefault())
		return QProcessEnvironment::systemEnvironment().value("GITHUB_PAT", GITHUB_PAT_DEFINED);

	return githubPatCustom();
}

QString PreferencesModel::fixedDecimalsForJS() const
{
	if(!fixedDecimals())
		return "\"\"";

	return QString::fromStdString(std::to_string(numDecimals()));
}

void PreferencesModel::setFixedDecimals(bool newFixedDecimals)
{
	if (fixedDecimals() == newFixedDecimals)
		return;

	Settings::setValue(Settings::FIXED_DECIMALS, newFixedDecimals);

	emit fixedDecimalsChanged(newFixedDecimals);
	emit fixedDecimalsChangedString(fixedDecimalsForJS());
}

void PreferencesModel::setNumDecimals(int newNumDecimals)
{
	if (numDecimals() == newNumDecimals)
		return;

	Settings::setValue(Settings::NUM_DECIMALS, newNumDecimals);

	emit numDecimalsChanged(newNumDecimals);

	if(fixedDecimals())
		emit fixedDecimalsChangedString(fixedDecimalsForJS());
}

void PreferencesModel::onUseDefaultPPIChanged(bool )
{
	if(customPPI() != defaultPPI())
		emit plotPPIChanged(plotPPI(), true);
}

void PreferencesModel::onCustomPPIChanged(int)
{
	if(!useDefaultPPI())
		emit plotPPIChanged(plotPPI(), true);
}

void PreferencesModel::onDefaultPPIChanged(int)
{

	if(useDefaultPPI())
		emit plotPPIChanged(plotPPI(), false);
}

#define SET_PREF_FUNCTION(TYPE, FUNC_NAME, GET_FUNC, NOTIFY, SETTING)	\
void PreferencesModel::FUNC_NAME(TYPE newVal)							\
{																		\
	if(GET_FUNC() == newVal) return;									\
	Settings::setValue(SETTING, newVal);								\
	emit NOTIFY(newVal);												\
}

#define SET_PREF_FUNCTION_EMIT_NO_ARG(TYPE, FUNC_NAME, GET_FUNC, NOTIFY, SETTING)	\
void PreferencesModel::FUNC_NAME(TYPE newVal)							\
{																		\
	if(GET_FUNC() == newVal) return;									\
	Settings::setValue(SETTING, newVal);								\
	emit NOTIFY();														\
}

void PreferencesModel::setCurrentThemeName(QString _currentThemeName)
{
	if (currentThemeName() == _currentThemeName) return;

	Settings::setValue(Settings::THEME_NAME, _currentThemeName);
	JaspTheme::setCurrentThemeFromName(_currentThemeName);

	emit currentThemeNameChanged(_currentThemeName);
}

SET_PREF_FUNCTION(				bool,		setExactPValues,			exactPValues,				exactPValuesChanged,			Settings::EXACT_PVALUES								)
SET_PREF_FUNCTION(				bool,		setNormalizedNotation,		normalizedNotation,			normalizedNotationChanged,		Settings::NORMALIZED_NOTATION						)
SET_PREF_FUNCTION(				bool,		setUseDefaultEditor,		useDefaultEditor,			useDefaultEditorChanged,		Settings::USE_DEFAULT_SPREADSHEET_EDITOR			)
SET_PREF_FUNCTION(				QString,	setCustomEditor,			customEditor,				customEditorChanged,			Settings::SPREADSHEET_EDITOR_NAME					)
SET_PREF_FUNCTION(				bool,		setUseDefaultPPI,			useDefaultPPI,				useDefaultPPIChanged,			Settings::PPI_USE_DEFAULT							)
SET_PREF_FUNCTION(				bool,		setDeveloperMode,			developerMode,				developerModeChanged,			Settings::DEVELOPER_MODE							)
SET_PREF_FUNCTION(				QString,	setDeveloperFolder,			developerFolder,			developerFolderChanged,			Settings::DEVELOPER_FOLDER							)
SET_PREF_FUNCTION(				int,		setCustomPPI,				customPPI,					customPPIChanged,				Settings::PPI_CUSTOM_VALUE							)
SET_PREF_FUNCTION(				float,		setRibbonBarHeightScale,	ribbonBarHeightScale,		ribbonBarHeightScaleChanged,	Settings::RIBBON_BAR_HEIGHT_SCALE					)
SET_PREF_FUNCTION(				bool,		setLogToFile,				logToFile,					logToFileChanged,				Settings::LOG_TO_FILE								)
SET_PREF_FUNCTION(				int,		setLogFilesMax,				logFilesMax,				logFilesMaxChanged,				Settings::LOG_FILES_MAX								)
SET_PREF_FUNCTION_EMIT_NO_ARG(	int,		setMaxFlickVelocity,		maxFlickVelocity,			maxFlickVelocityChanged,		Settings::QML_MAX_FLICK_VELOCITY					)
SET_PREF_FUNCTION(				bool,		setModulesRemember,			modulesRemember,			modulesRememberChanged,			Settings::MODULES_REMEMBER							)
SET_PREF_FUNCTION(				QString,	setCranRepoURL,				cranRepoURL,				cranRepoURLChanged,				Settings::CRAN_REPO_URL								)
SET_PREF_FUNCTION(				bool,		setGithubPatUseDefault,		githubPatUseDefault,		githubPatUseDefaultChanged,		Settings::GITHUB_PAT_USE_DEFAULT					)
SET_PREF_FUNCTION(				QString,	setPlotBackground,			plotBackground,				plotBackgroundChanged,			Settings::IMAGE_BACKGROUND							)
SET_PREF_FUNCTION(				bool,		setUseNativeFileDialog,		useNativeFileDialog,		useNativeFileDialogChanged,		Settings::USE_NATIVE_FILE_DIALOG					)
SET_PREF_FUNCTION(				bool,		setDisableAnimations,		disableAnimations,			disableAnimationsChanged,		Settings::DISABLE_ANIMATIONS						)
SET_PREF_FUNCTION(				bool,		setGenerateMarkdown,		generateMarkdown,			generateMarkdownChanged,		Settings::GENERATE_MARKDOWN_HELP					)
SET_PREF_FUNCTION_EMIT_NO_ARG(	QString,	setInterfaceFont,			interfaceFont,				interfaceFontChanged,			Settings::INTERFACE_FONT							)
SET_PREF_FUNCTION(				QString,	setCodeFont,				codeFont,					codeFontChanged,				Settings::CODE_FONT									)
SET_PREF_FUNCTION(				QString,	setResultFont,				resultFont,					resultFontChanged,				Settings::RESULT_FONT								)
SET_PREF_FUNCTION(				int,		setMaxEngines,				maxEngines,					maxEnginesChanged,				Settings::MAX_ENGINE_COUNT							)
SET_PREF_FUNCTION(				bool,		setWindowsNoBomNative,		windowsNoBomNative,			windowsNoBomNativeChanged,		Settings::WINDOWS_NO_BOM_NATIVE						)
SET_PREF_FUNCTION(				int,		setWindowsChosenCodePage,	windowsChosenCodePage,		windowsChosenCodePageChanged,	Settings::WINDOWS_CHOSEN_CODEPAGE					)
SET_PREF_FUNCTION(				bool,		setDbShowWarning,			dbShowWarning,				dbShowWarningChanged,			Settings::DB_SHOW_WARNING							)
SET_PREF_FUNCTION(				QString,	setDataLabelNA,				dataLabelNA,				dataLabelNAChanged,				Settings::DATA_LABEL_NA								)
SET_PREF_FUNCTION(				bool,		setGuiQtTextRender,			guiQtTextRender,			guiQtTextRenderChanged,			Settings::GUI_USE_QT_TEXTRENDER						)
SET_PREF_FUNCTION(				bool,		setReportingMode,			reportingMode,				reportingModeChanged,			Settings::REPORT_SHOW								)
SET_PREF_FUNCTION_EMIT_NO_ARG(	bool,		setShowRSyntax,				showRSyntax,				showRSyntaxChanged,				Settings::SHOW_RSYNTAX								)
SET_PREF_FUNCTION_EMIT_NO_ARG(	bool,		setShowAllROptions,			showAllROptions,			showAllROptionsChanged,			Settings::SHOW_ALL_R_OPTIONS						)
SET_PREF_FUNCTION(				bool,		setShowRSyntaxInResults,	showRSyntaxInResults,		showRSyntaxInResultsChanged,	Settings::SHOW_RSYNTAX_IN_RESULTS					)
SET_PREF_FUNCTION(				bool,		setALTNavModeActive,		ALTNavModeActive,			ALTNavModeActiveChanged,		Settings::ALTNAVMODE_ACTIVE							)
SET_PREF_FUNCTION(				bool,		setOrderByValueByDefault,	orderByValueByDefault,		orderByValueByDefaultChanged,	Settings::ORDER_BY_VALUE_BY_DEFAULT					)
SET_PREF_FUNCTION(				bool,		setCheckUpdatesAskUser,		checkUpdatesAskUser,		checkUpdatesAskUserChanged,		Settings::CHECK_UPDATES_ASK_USER					)
SET_PREF_FUNCTION(				bool,		setCheckUpdates,			checkUpdates,				checkUpdatesChanged,			Settings::CHECK_UPDATES								)
SET_PREF_FUNCTION(				int,		setMaxScaleLevels,			maxScaleLevels,				maxScaleLevelsChanged,			Settings::MAX_SCALE_LEVELS							)
SET_PREF_FUNCTION(				bool,		setPdfLandscape,			pdfLandscape,				pdfLandscapeChanged,			Settings::PDF_LANDSCAPE								)
SET_PREF_FUNCTION(				int,		setPdfPageSize,				pdfPageSize,				pdfPageSizeChanged,				Settings::PDF_PAGESIZE								)
SET_PREF_FUNCTION(				bool,		setDirectLibpathEnabled,	directLibpathEnabled,		directLibpathEnabledChanged,	Settings::DIRECT_LIBPATH_ENABLED					)
SET_PREF_FUNCTION(				QString,	setDirectDevModName,		directDevModName,			directDevModNameChanged,		Settings::DIRECT_DEVMOD_NAME						)
SET_PREF_FUNCTION(				QString,	setLocalConfigurationPATH,	localConfigurationPATH,		localConfigurationPATHChanged,	Settings::LOCAL_CONFIGURATION_PATH					)
SET_PREF_FUNCTION(				bool,   	setRemoteConfiguration, 	remoteConfiguration,		remoteConfigurationChanged,     Settings::REMOTE_CONFIGURATION  					)
SET_PREF_FUNCTION(				QString,	setRemoteConfigurationURL,	remoteConfigurationURL,		remoteConfigurationURLChanged,	Settings::REMOTE_CONFIGURATION_URL					)
SET_PREF_FUNCTION(				bool,   	setUseConfigurationFile, 	useConfigurationFile,		useConfigurationFileChanged,    Settings::USE_CONFIGURATION_FILE  					)
SET_PREF_FUNCTION(				bool,   	setStartMaximized,			startMaximized,				startMaximizedChanged,			Settings::START_MAXIMIZED		  					)
SET_PREF_FUNCTION(				bool,   	setStoreStateEtc,			storeStateEtc,				storeStateEtcChanged,			Settings::STORE_STATE_ETC		  					)
SET_PREF_FUNCTION(				bool,   	setAutoSaveAtAll,			autoSaveAtAll,				autoSaveAtAllChanged,			Settings::AUTOSAVE_ON			  					)
SET_PREF_FUNCTION(				int,		setAutoSaveIntervalSec,		autoSaveIntervalSec,		autoSaveIntervalSecChanged,		Settings::AUTOSAVE_INTERVAL_SEC	  					)


void PreferencesModel::setGithubPatCustom(QString newPat)
{
	if (githubPatCustom() == newPat)
		return;

	Settings::setValue(Settings::GITHUB_PAT_CUSTOM, encrypt(newPat));

	emit githubPatCustomChanged();
}

void PreferencesModel::setWhiteBackground(bool newWhiteBackground)
{
	if (whiteBackground() == newWhiteBackground)
		return;

	setPlotBackground(newWhiteBackground ? "white" : "transparent");
}

void PreferencesModel::setDefaultPPI(int defaultPPI)
{
	if (_defaultPPI == defaultPPI)
		return;

	_defaultPPI = defaultPPI;
	emit defaultPPIChanged(_defaultPPI);
}

void PreferencesModel::setUiScale(double newUiScale)
{
	newUiScale = std::min(3.0, std::max(0.2, newUiScale));

	if (std::abs(uiScale() - newUiScale) < 0.001)
		return;

	Settings::setValue(Settings::UI_SCALE, newUiScale);
	_uiScale = newUiScale;

	emit uiScaleChanged();
}

void PreferencesModel::setModulesRemembered(QStringList newModulesRemembered)
{
	if (modulesRemembered() == newModulesRemembered)
		return;

	Settings::setValue(Settings::MODULES_REMEMBERED, newModulesRemembered.join('|'));
	emit modulesRememberedChanged();
}

void PreferencesModel::setSafeGraphics(bool newSafeGraphics)
{
	if (safeGraphics() == newSafeGraphics)
		return;

	Settings::setValue(Settings::SAFE_GRAPHICS_MODE, newSafeGraphics);
	emit modulesRememberChanged(newSafeGraphics);

	MessageForwarder::showWarning(tr("Safe Graphics mode changed"), tr("You've changed the Safe Graphics mode of JASP, for this option to take effect you need to restart JASP"));

	emit safeGraphicsChanged(newSafeGraphics);
}

void PreferencesModel::zoomIn()
{
	setUiScale(uiScale() + 0.1);
}

void PreferencesModel::zoomOut()
{
	if (uiScale() >= 0.2)
		setUiScale(uiScale() - 0.1);
}

void PreferencesModel::zoomReset()
{
	setUiScale(1.0);
}

void PreferencesModel::removeEmptyValue(QString value)
{
	QStringList currentValues = emptyValues();
	if(currentValues.contains(value))
	{
		currentValues.removeAll(value);
		_setEmptyValues(currentValues);
	}
}

void PreferencesModel::addEmptyValue(QString value)
{
	QStringList currentValues = emptyValues();
	if(!currentValues.contains(value))
	{
		currentValues.append(value);
		_setEmptyValues(currentValues);
	}
}

void PreferencesModel::resetEmptyValues()
{
	QStringList defaultValues = _splitValues(Settings::defaultEmptyValues);
	_setEmptyValues(defaultValues);
}

void PreferencesModel::setThresholdScale(int newThresholdScale)
{
	if (thresholdScale() == newThresholdScale)
		return;

	Settings::setValue(Settings::THRESHOLD_SCALE, newThresholdScale);
	emit thresholdScaleChanged(newThresholdScale);

}

void PreferencesModel::_loadDatabaseFont()
{
	QFontDatabase fontDatabase;

	fontDatabase.addApplicationFont(":/fonts/FreeSans.ttf");
	fontDatabase.addApplicationFont(":/fonts/FiraCode-Retina.ttf");

	_allFonts = _allCodeFonts = _allResultFonts = _allInterfaceFonts = fontDatabase.families();
	_allCodeFonts.removeAll(defaultCodeFont());
	_allResultFonts.removeAll(defaultResultFont());
	_allInterfaceFonts.removeAll(defaultInterfaceFont());
}

QString PreferencesModel::_checkFontList(QString fonts) const
{
	if (fonts.contains(","))
		// If it is a list of fonts.
		// Select the first one which is available.
		for (QString font : fonts.split(","))
		{
			if (_allFonts.contains(font.remove('"')))
				return font;
		}

	return fonts;
}

QString PreferencesModel::defaultResultFont() const
{
	return _checkFontList(Settings::defaultValue(Settings::RESULT_FONT).toString());
}

QString PreferencesModel::resultFont(bool forWebEngine) const
{
	QString font		= Settings::value(Settings::RESULT_FONT).toString(),
			defaultFont = Settings::defaultValue(Settings::RESULT_FONT).toString();

	if (font.isEmpty()) font = defaultFont;

	if (forWebEngine)
	{
		// for WebEngine, if the font is the default one (that is a list of fonts), then use directly this list.
		// the css is then exacly the same as it used to be and we are sure that the user gets the same rendering.
		// If the font starts with a dot, then it needs extra quote for the WebEngine.
		if (font.startsWith("."))
			font = '"' + font + '"';
	}
	else
		font = _checkFontList(font);

	return font;
}

QString PreferencesModel::interfaceFont() const
{
	QString font = Settings::value(Settings::INTERFACE_FONT).toString();

	if (font.isEmpty()) font = defaultInterfaceFont();

	return font;
}

QString PreferencesModel::codeFont() const
{
	QString font = Settings::value(Settings::CODE_FONT).toString();

	if (font.isEmpty()) font = defaultCodeFont();

	return font;
}

QString PreferencesModel::defaultInterfaceFont() const
{
	return Settings::defaultValue(Settings::INTERFACE_FONT).toString();
}

QString PreferencesModel::defaultCodeFont() const
{
	return Settings::defaultValue(Settings::CODE_FONT).toString();
}

void PreferencesModel::resetRememberedModules(bool setToRemember) 
{
	setModulesRemembered(!setToRemember ? QStringList({}) : RibbonModel::singleton()->getModulesEnabled());
}

void PreferencesModel::dataLabelNAChangedSlot(QString dataLabelNA)
{
	EmptyValues::setDisplayString(fq(dataLabelNA));
}

void PreferencesModel::onGuiQtTextRenderChanged(bool newGuiQtTextRenderSetting)
{
	QQuickWindow::setTextRenderType(newGuiQtTextRenderSetting ? QQuickWindow::QtTextRendering : QQuickWindow::NativeTextRendering);

	MessageForwarder::showWarning(tr("Text rendering setting changed"), tr("The textrendering setting has been changed, this will only take full effect after JASP is restarted."));
}

void PreferencesModel::currentThemeNameHandler()
{
	setCurrentThemeName(JaspTheme::currentTheme()->themeName());
}

void PreferencesModel::_setEmptyValues(const QStringList& values)
{
	if (values != emptyValues())
	{
		Settings::setValue(Settings::EMPTY_VALUES_LIST, values.join("|"));
		emit emptyValuesChanged();
	}
}

QStringList PreferencesModel::_splitValues(const QString &values) const
{
	QStringList items = values.split("|");
	std::set<QString> orderedValues; // use std::set to order the items
	for (const QString& item : items)
		orderedValues.insert(stripFirstAndLastChar(item,"\""));

	return QStringList(orderedValues.begin(), orderedValues.end());

}

void PreferencesModel::setDirectLibpathFolder(QString libpath)
{
	if (libpath != "")
	{
		Settings::setValue(Settings::DIRECT_LIBPATH_FOLDER, libpath);
		emit directLibpathFolderChanged();
	}
}

void PreferencesModel::setEngineSandbox(bool engineSandbox) {
	if(engineSandbox != PreferencesModel::engineSandbox()) {
		Settings::setValue(Settings::ENGINE_SANDBOX, engineSandbox);

#ifdef _WIN32
		MessageForwarder::showWarning(tr("Engine Sandbox setting changed"), tr("The Engine Sandbox setting has been changed, this will only take full effect after JASP is restarted."));
#endif
		emit engineSandboxChanged(engineSandbox);
	}
}
